﻿namespace DotNetStar.OpenSDK
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Xml.Linq;
    using System.Text;
    using System.Dynamic;
    using Newtonsoft.Json;
    using System.Collections.Specialized;

    /// <summary>
    /// 表示使用小写字母作为键并按照升序排列的键值对集合对象。
    /// </summary>
    public sealed class KeyValueCollection : DynamicObject, IEnumerable<KeyValuePair<string, object>>,IReadOnlyDictionary<string,object>,ICollection<KeyValuePair<string,object>>
    {
        private IDictionary<string, object> _keyValuePairs;

        #region 构造函数
        /// <summary>
        /// 初始化 <see cref="KeyValueCollection"/> 类的新实例。
        /// </summary>
        public KeyValueCollection() => Initialize();

        /// <summary>
        /// 使用一个 <see cref="KeyValueCollection"/> 对象初始化 <see cref="KeyValueCollection"/> 类的新实例。
        /// </summary>
        /// <param name="keyValueCollection">一个键值对集合。</param>
        public KeyValueCollection(KeyValueCollection keyValueCollection) : this() => Import(keyValueCollection);

        /// <summary>
        /// 使用键值对集合初始化 <see cref="KeyValueCollection"/> 类的新实例。
        /// </summary>
        /// <param name="keyValuePairs">键值对集合。</param>
        public KeyValueCollection(IEnumerable<KeyValuePair<string, object>> keyValuePairs) : this() => Import(keyValuePairs);

        /// <summary>
        /// 使用一个 <see cref="NameValueCollection"/> 对象初始化 <see cref="KeyValueCollection"/> 类的新实例。
        /// </summary>
        /// <param name="nameValueCollection">一个 <see cref="NameValueCollection"/> 实例。</param>
        public KeyValueCollection(NameValueCollection nameValueCollection) : this() => Import(nameValueCollection);

        /// <summary>
        /// 使用键值对对象初始化 <see cref="KeyValueCollection"/> 类的新实例。
        /// </summary>
        /// <param name="keyValueObject">一个键值对对象，可以是实体对象或匿名对象。</param>
        public KeyValueCollection(object keyValueObject) : this() => Import(keyValueObject);

        /// <summary>
        /// 使用键值对形式的字符串初始化 <see cref="KeyValueCollection"/> 类的新实例。
        /// </summary>
        /// <param name="keyValueString">一个键值对形式的字符串；仅支持 xml 字符串、json 字符串以及用"&amp;"符号链接起来的 key=value 形式的字符串(key1=value1&amp;key2=value2&amp;key3=value3...)。</param>
        public KeyValueCollection(string keyValueString) : this() => Import(keyValueString);
        #endregion

        #region 索引器
        /// <summary>
        /// 获取或设置与指定键关联的值。
        /// </summary>
        /// <param name="key">要设置值或获取值的键。</param>
        /// <returns>
        /// 获取操作时，若指定的键不存在，则返回 <c>null</c>；否则返回与键关联的值。
        /// 设置操作时，若指定的键不存在，则新增；否则使用新值覆盖。
        /// </returns>
        public object this[string key]
        {
            get
            {
                if (_keyValuePairs.TryGetValue(key, out object value))
                {
                    return value;
                }
                return null;
            }
            set => this.Set(key, value);

        }

        /// <summary>
        /// 获取或设置指定索引的键值对。
        /// </summary>
        /// <value>
        /// 一个键值对。
        /// </value>
        /// <param name="index">索引的值</param>
        /// <exception cref="ArgumentOutOfRangeException">索引超出数组界限。</exception>
        /// <returns></returns>
        public KeyValuePair<string, object> this[int index]
        {
            get => _keyValuePairs.ElementAt(index);
            set
            {
                if (this.Contains(value))
                {
                    this[value.Key] = value.Value;
                }
                else
                {
                    this.Add(value);
                }
            }
        }
        #endregion

        #region Initialize
        /// <summary>
        /// 对当前所有键值对对象进行初始化。
        /// </summary>
        public void Initialize() => _keyValuePairs = new SortedDictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        #endregion

        #region 成员
        /// <summary>
        /// 设置指定键的值。若指定的键不存在，则新增；否则覆盖值。
        /// </summary>
        /// <param name="key">要设置的键。</param>
        /// <param name="value">要设置的键对应的值。</param>
        public void Set(string key, object value)
        {
            if (this.ContainsKey(key))
            {
                _keyValuePairs[key] = value;
            }
            else
            {
                Add(key, value);
            }
        }


        /// <summary>
        /// 判断当前集合是否包含指定键。
        /// </summary>
        /// <param name="key">要判断的键。</param>
        /// <returns>
        ///   如果包含在集合中，则为 <c>true</c>；否则为 <c>false</c>。
        /// </returns>
        public bool ContainsKey(string key) => _keyValuePairs.ContainsKey(key);

        /// <summary>
        /// 移除指定的键。
        /// </summary>
        /// <param name="key">要移除的键。</param>
        /// <returns>如果成功移除则返回 <c>true</c>；否则返回 <c>false</c>。如果指定的键不存在也返回 <c>false</c>。</returns>
        public bool Remove(string key)
        => _keyValuePairs.Remove(key);

        /// <summary>
        /// 清除所有的键值对。
        /// </summary>
        public void Clear() => _keyValuePairs.Clear();

        /// <summary>
        /// 获取所有的键集合。
        /// </summary>
        public IEnumerable<string> Keys => _keyValuePairs.Keys;

        /// <summary>
        /// 获取所有的值集合。
        /// </summary>
        public IEnumerable<object> Values => _keyValuePairs.Values;

        /// <summary>
        /// 获取当前集合的数量。
        /// </summary>
        public int Count => _keyValuePairs.Count();

        /// <summary>
        /// 获取一个布尔值，表示当前集合是否是只读的。
        /// </summary>
        public bool IsReadOnly { get; }

        /// <summary>
        /// 添加指定键和值。若指定的键已存在，则抛出异常。
        /// </summary>
        /// <param name="key">要添加的键的名称。</param>
        /// <param name="value">要添加对应指定键的值。</param>
        /// <exception cref="InvalidOperationException">键已存在集合中</exception>
        public void Add(string key, object value)
        {
            if (ContainsKey(key))
            {
                throw new InvalidOperationException("指定的键已存在集合中");
            }
            _keyValuePairs.Add(key.ToLower(), value);
        }

        /// <summary>
        /// 尝试获取指定键的值。
        /// </summary>
        /// <param name="key">要获取的键。</param>
        /// <param name="value">当该方法返回 <c>true</c> 时将返回指定的键对应的值；否则将返回初始化的值。</param>
        /// <returns>
        /// 若包含指定的键，则返回 <c>true</c>；否则为 <c>false</c>。
        /// </returns>
        public bool TryGetValue(string key, out object value) => _keyValuePairs.TryGetValue(key.ToLower(), out value);

        /// <summary>
        /// 添加一个键值对。
        /// </summary>
        /// <param name="item">需要添加的键值对。</param>
        public void Add(KeyValuePair<string, object> item) => Add(item.Key, item.Value);

        /// <summary>
        /// 表示当前集合是否包含指定项。
        /// </summary>
        /// <param name="item">一个键值对的项。</param>
        /// <returns>
        /// 若指定的键和值完全匹配，则返回 <c>true</c>；否则返回 <c>false</c>。
        /// </returns>
        public bool Contains(KeyValuePair<string, object> item) => _keyValuePairs.Contains(item);
        /// <summary>
        /// 复制当前集合到指定的数组中，同时指定开始复制的数组的索引值。
        /// </summary>
        /// <param name="array">需要从当前的集合复制的目标数组。</param>
        /// <param name="arrayIndex">目标数组开始复制的一个大于 0 的索引。</param>
        public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) => _keyValuePairs.CopyTo(array, arrayIndex);
        /// <summary>
        /// 移除指定的项。
        /// </summary>
        /// <param name="item">要从当前集合中移除的项。</param>
        /// <returns>
        /// 若成功移除，则返回 <c>true</c>；否则返回 <c>false</c>。若当前指定的项的键不存在，也返回 <c>false</c>。
        /// </returns>
        public bool Remove(KeyValuePair<string, object> item) => _keyValuePairs.Remove(item);
        /// <summary>
        /// 获取当前集合的迭代器。
        /// </summary>
        /// <returns>
        /// 一个可用于迭代当前集合的迭代器。
        /// </returns>
        public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => _keyValuePairs.GetEnumerator();

        /// <summary>
        /// 返回当前集合的迭代器。
        /// </summary>
        /// <returns>
        /// 一个用于迭代当前集合的迭代器。
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            yield return _keyValuePairs;
        }
        #endregion

        #region Import        
        /// <summary>
        /// 导入已知的 <see cref="KeyValueCollection"/> 实例。若相同的键存在，则会使用新导入的值覆盖。
        /// </summary>
        /// <param name="keyValueCollection">要导入的 <see cref="KeyValueCollection"/> 实例。</param>
        /// <exception cref="ArgumentNullException">keyValueCollection 是 null。</exception>
        public void Import(KeyValueCollection keyValueCollection)
        {
            if (keyValueCollection == null)
            {
                throw new ArgumentNullException(nameof(keyValueCollection));
            }

            Import(keyValueCollection.ToDictionary());
        }

        /// <summary>
        /// 导入一个已知的键值对集合。若相同的键存在，则会使用新导入的值覆盖。
        /// </summary>
        /// <param name="keyValuePairs">要导入的键值对集合。</param>
        /// <exception cref="ArgumentNullException">keyValuePairs 是 null。</exception>
        public void Import(IEnumerable<KeyValuePair<string, object>> keyValuePairs)
        {
            if (keyValuePairs == null)
            {
                throw new ArgumentNullException(nameof(keyValuePairs));
            }

            foreach (var item in keyValuePairs)
            {
                Set(item.Key, item.Value);
            }
        }

        /// <summary>
        /// 导入已知的 <see cref="NameValueCollection" /> 键值对实例集合。若相同的键存在，则会使用新导入的值覆盖。
        /// </summary>
        /// <param name="nameValueCollection">要导入的 <see cref="NameValueCollection" /> 实例。</param>
        /// <exception cref="ArgumentNullException">nameValueCollection 是 null。</exception>
        public void Import(NameValueCollection nameValueCollection)
        {
            if (nameValueCollection == null)
            {
                throw new ArgumentNullException(nameof(nameValueCollection));
            }

            var newKeyValuePairs = new Dictionary<string, object>();
            for (int i = 0; i < nameValueCollection.Count; i++)
            {
                newKeyValuePairs[nameValueCollection.GetKey(i)] = nameValueCollection[i];
            }
            Import(newKeyValuePairs);
        }


        /// <summary>
        /// 导入指定对象。该对象可以是实体对象或匿名对象。
        /// </summary>
        /// <param name="keyValueObject">要导入的对象。</param>
        /// <exception cref="NotSupportedException">给定的对象必须是实体对象或匿名对象。</exception>
        /// <returns>导入成功则为 <c>true</c>；否则返回 <c>false</c> 或抛出异常。</returns>
        public void Import(object keyValueObject)
        {
            if (keyValueObject == null)
            {
                return;
            }

            try
            {
                if (keyValueObject is KeyValueCollection collectionObj)
                {
                    Import(collectionObj);
                    return;
                }

                Import(keyValueObject.GetType().GetRuntimeProperties()
                    .Where(m => !m.IsDefined(typeof(ArgumentIgnoreAttribute)))
                    .Where(m => m.GetValue(keyValueObject) != null && !string.IsNullOrWhiteSpace(m.GetValue(keyValueObject).ToString()))
                  .Select(m => new KeyValuePair<string, object>(
                      (m.IsDefined(typeof(ArgumentNameAttribute)) ? m.GetCustomAttribute<ArgumentNameAttribute>().Name : m.Name), m.GetValue(keyValueObject))));
            }
            catch (Exception ex)
            {
                throw new NotSupportedException("给定的对象必须是实体对象或匿名对象", ex);
            }
        }

        /// <summary>
        /// 导入已知的键值对形式的字符串。若相同的键存在，则会使用新导入的值覆盖。
        /// </summary>
        /// <param name="keyValueCollectionString">导入的字符串；仅支持 xml 字符串、json 字符串以及用"&amp;"符号链接起来的 key=value 形式的字符串(key1=value1&amp;key2=value2&amp;key3=value3...)。</param>
        /// <exception cref="ArgumentNullException">keyValueCollectionString 是 null。</exception>
        public void Import(string keyValueCollectionString)
        {
            if (keyValueCollectionString == null)
            {
                throw new ArgumentNullException(nameof(keyValueCollectionString));
            }

            keyValueCollectionString = keyValueCollectionString.Trim();

            if ((keyValueCollectionString.StartsWith("{") && keyValueCollectionString.EndsWith("}")))
            {
                ImportFromJsonString(keyValueCollectionString, Encoding.UTF8);
            }
            else if (keyValueCollectionString.StartsWith("<") && keyValueCollectionString.EndsWith(">"))
            {
                ImportFromXml(keyValueCollectionString);
            }
            else
            {
                ImportFromKeyValueString(keyValueCollectionString);
            }
        }

        /// <summary>
        /// 从 xml 字符串中导入。
        /// </summary>
        /// <param name="xmlString">要导入的 xml 形式字符串。</param>
        /// <returns>导入成功则为 <c>true</c>；否则返回 <c>false</c> 或抛出异常。</returns>
        /// <exception cref="ArgumentNullException">xmlString 是 null。</exception>
        /// <exception cref="NotSupportedException">给定的字符串不是标准的 xml 字符串格式</exception>        
        internal void ImportFromXml(string xmlString)
        {
            if (xmlString == null)
            {
                throw new ArgumentNullException(nameof(xmlString));
            }

            try
            {
                var xDoc = XDocument.Parse(xmlString);
                Import(xDoc.Root.Elements().Select(m => new KeyValuePair<string, object>(m.Name.LocalName, Convert.ChangeType(m.Value, m.Value.GetType()))));
            }
            catch (Exception ex)
            {
                throw new NotSupportedException("给定的字符串不是标准的 xml 字符串格式", ex);
            }
        }


        /// <summary>
        /// 导入指定的符合 “&amp;” 符号串联起来的键值对字符串。
        /// </summary>
        /// <param name="keyValueString">用 “&amp;” 符号串联起来的键值对字符串。</param>
        /// <returns>导入成功则为 <c>true</c>；否则返回 <c>false</c> 或抛出异常。</returns>
        /// <exception cref="ArgumentNullException">keyValueString 是 null</exception>
        /// <exception cref="NotSupportedException">给定的字符串不是规定的 key1=value&amp;key2=value2.... 的字符串形式</exception>
        internal void ImportFromKeyValueString(string keyValueString)
        {
            if (keyValueString == null)
            {
                throw new ArgumentNullException(nameof(keyValueString));
            }

            if (keyValueString.IndexOf('?') == 0)
            {
                keyValueString = keyValueString.Substring(1);
            }

            var querySpliter = keyValueString.Split('&');
            if (querySpliter == null)
            {
                throw new NotSupportedException("给定的字符串不是规定的 key1=value&key2=value2.... 的字符串形式");
            }

            var dic = new Dictionary<string, object>(querySpliter.Length);

            foreach (var item in querySpliter)
            {
                var keyValue = item.Split('=');
                if (keyValue == null)
                {
                    continue;
                }
                var key = keyValue[0];
                var value = keyValue[1];

                dic.Add(key, value);
            }
            Import(dic);
        }

        /// <summary>
        /// 导入指定的 json 字符串。
        /// </summary>
        /// <param name="jsonString">The json string.</param>
        /// <param name="encoding">The encoding.</param>
        internal void ImportFromJsonString(string jsonString, Encoding encoding = default(Encoding))
        => Import(JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonString));


        #endregion

        #region 转换
        /// <summary>
        /// 转换成一个键值对集合。
        /// </summary>
        /// <returns>数量、键值顺序相同的键值对集合。</returns>
        public List<KeyValuePair<string, object>> ToList()
            => new List<KeyValuePair<string, object>>(_keyValuePairs.Select(m => new KeyValuePair<string, object>(m.Key, m.Value)));

        /// <summary>
        /// 转换成一个字典。
        /// </summary>
        /// <returns>数量、键值顺序相同的字典。</returns>
        public IDictionary<string, object> ToDictionary() => _keyValuePairs;

        /// <summary>
        /// 转换成一个 xml 形式的字符串。注意：该字符串并不是标准的 xml 标准格式的字符串。
        /// </summary>
        /// <remarks>字符串类型会由 CDATA ，如&lt;string>[!CDATA[value]]&lt;/string></remarks>
        /// <example>
        /// &lt;xml>
        ///     &lt;key1>value1&lt;key1>
        ///     &lt;key2>value1&lt;key2>
        ///     &lt;key3>value1&lt;key3>
        ///     ....
        /// &lt;/xml>
        /// </example>
        /// <returns>符合 xml 格式的字符串形式。</returns>
        public string ToXml()
        {
            var xmlBuilder = new StringBuilder();
            xmlBuilder.AppendLine("<xml>");
            foreach (var item in _keyValuePairs)
            {
                if (item.Value == null)
                {
                    continue;
                }
                if (item.Value.GetType() == typeof(string))
                {
                    xmlBuilder.AppendFormat("<{0}><![CDATA[{1}]]></{0}>", item.Key.ToLower(), item.Value);
                }
                else
                {
                    xmlBuilder.AppendFormat("<{0}>{1}</{0}>", item.Key.ToLower(), item.Value);
                }
                xmlBuilder.AppendLine();
            };
            xmlBuilder.AppendLine("</xml>");
            return xmlBuilder.ToString();
        }

        /// <summary>
        /// 转换成一个简易的 json 形式的字符串。不支持复杂的 json 字符串。
        /// </summary>
        /// <returns>一个符合 json 格式的字符串。</returns>
        public string ToJson() => ToJson(default(JsonSerializerSettings));

        /// <summary>
        /// 转换成一个简易的 json 形式的字符串。不支持复杂的 json 字符串。
        /// </summary>
        /// <param name="serializerSettings">json 序列化器的配置。</param>
        /// <returns></returns>
        public string ToJson(JsonSerializerSettings serializerSettings) => JsonConvert.SerializeObject(_keyValuePairs, serializerSettings);

        /// <summary>
        /// 转换成指定类型的对象。标记 <see cref="ArgumentIgnoreAttribute"/> 特性的属性将忽略。
        /// </summary>
        /// <typeparam name="T">要转换的对象类型。</typeparam>
        /// <returns>要转换的对象类型，同名属性有相应键的值。</returns>
        public T ToObject<T>() => (T)ToObject(typeof(T));

        /// <summary>
        /// 转换成指定类型的对象。标记 <see cref="ArgumentIgnoreAttribute"/> 特性的属性将忽略。
        /// </summary>
        /// <returns>要转换的对象类型，同名属性有相应键的值。</returns>
        public object ToObject(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var instance = Activator.CreateInstance(type);
            foreach (var item in instance.GetType().GetRuntimeProperties())
            {
                if (item.IsDefined(typeof(ArgumentIgnoreAttribute)))
                {
                    continue;
                }

                foreach (var keyValue in _keyValuePairs)
                {
                    var name = item.Name.ToLower();
                    if (item.IsDefined(typeof(ArgumentNameAttribute)))
                    {
                        name = item.GetCustomAttribute<ArgumentNameAttribute>().Name;
                    }

                    if (name == keyValue.Key.ToLower())
                    {
                        item.SetValue(instance, keyValue.Value);
                        continue;
                    }
                }
            }
            return instance;
        }

        /// <summary>
        /// 转换成 <see cref="NameValueCollection"/> 实例。
        /// </summary>
        /// <returns>相同键值对的 <see cref="NameValueCollection"/> 实例。</returns>
        public NameValueCollection ToNameValueCollection()
        {
            NameValueCollection nameValueCollection = new NameValueCollection(_keyValuePairs.Count);
            foreach (var item in _keyValuePairs)
            {
                nameValueCollection.Add(item.Key, item.Value?.ToString());
            }
            return nameValueCollection;
        }

        /// <summary>
        /// 将现有的键值对集合转换成由 “&amp;” 字符连接起来，并将键转换成小写字母、排除值是 null 或空白字符串、并按照键的升序排列的字符串。
        /// </summary>
        /// <example>
        /// key1=value1&amp;key2=value2&amp;key3=value3&amp;......
        /// </example>
        /// <returns>
        /// 一个由“&amp;”符号将所有键值对连接起来并按照字母升序排列、过滤值是 null 或空白字符串，并将键转换成小写字母的字符串形式。
        /// </returns>
        public override string ToString()
        {
            if (_keyValuePairs.Count == 0)
            {
                return string.Empty;
            }

            var filterList = _keyValuePairs
               .Select(m => new KeyValuePair<string, object>(m.Key.ToLower(), m.Value))
               .Where(m => m.Value != null && !string.IsNullOrWhiteSpace(m.Value.ToString()));

            if (!filterList.Any())
            {
                return string.Empty;
            }
            return filterList.Select(kv => $"{kv.Key}={kv.Value}").Aggregate((pre, next) => $"{pre}&{next}");
        }
        #endregion

        #region dynamic
        /// <summary>
        /// 获取动态成员的所有名称。
        /// </summary>
        /// <returns>
        /// 由小写字母升序排列的成员集合。
        /// </returns>
        public override IEnumerable<string> GetDynamicMemberNames() => this.Keys;

        /// <summary>
        /// Provides the implementation for operations that get member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class can override this method to specify dynamic behavior for operations such as getting a value for a property.
        /// </summary>
        /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
        /// <param name="result">The result of the get operation. For example, if the method is called for a property, you can assign the property value to result.</param>
        /// <returns>
        /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a run-time exception is thrown.)
        /// </returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = this[binder.Name];
            return true;
        }

        /// <summary>
        /// Provides the implementation for operations that set member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class can override this method to specify dynamic behavior for operations such as setting a value for a property.
        /// </summary>
        /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
        /// <param name="value">The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class, the value is "Test".</param>
        /// <returns>
        /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.)
        /// </returns>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            Set(binder.Name, value);
            return true;
        }
        /// <summary>
        /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic.
        /// </summary>
        /// <param name="binder">Provides information about the deletion.</param>
        /// <returns>
        /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.)
        /// </returns>
        public override bool TryDeleteMember(DeleteMemberBinder binder) => this.Remove(binder.Name);

        /// <summary>
        /// Provides the implementation for operations that invoke an object. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class can override this method to specify dynamic behavior for operations such as invoking an object or a delegate.
        /// </summary>
        /// <param name="binder">Provides information about the invoke operation.</param>
        /// <param name="args">The arguments that are passed to the object during the invoke operation. For example, for the sampleObject(100) operation, where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class, args[0] is equal to 100.</param>
        /// <param name="result">The result of the object invocation.</param>
        /// <returns>
        /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.
        /// </returns>
        /// <exception cref="ArgumentException">方法调用不正确。必须要求2个参数并且第一个参数必须是 string 类型。</exception>
        public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
        {
            if (binder.CallInfo.ArgumentCount == 0)
            {
                result = null;
                return false;
            }

            if (binder.CallInfo.ArgumentCount == 1)
            {
                result = this[args[0].ToString().ToLower()];
                return true;
            }

            var key = args[0].ToString().ToLower();
            var value = args[1];

            this[key] = value;
            result = value;
            return true;
        }

        /// <summary>
        /// Provides the implementation for operations that invoke a member. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class can override this method to specify dynamic behavior for operations such as calling a method.
        /// </summary>
        /// <param name="binder">Provides information about the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the statement sampleObject.SampleMethod(100), where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class, binder.Name returns "SampleMethod". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
        /// <param name="args">The arguments that are passed to the object member during the invoke operation. For example, for the statement sampleObject.SampleMethod(100), where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class, args[0] is equal to 100.</param>
        /// <param name="result">The result of the member invocation.</param>
        /// <returns>
        /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.)
        /// </returns>
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            try
            {
                switch (binder.Name.ToLower())
                {
                    case "xml":
                        result = ToXml();
                        break;
                    case "json":
                        result = ToJson();
                        break;
                    case "list":
                        result = ToList();
                        break;
                    case "dic":
                        result = ToDictionary();
                        break;
                    case "num":
                        result = Count;
                        break;
                    case "str":
                        result = ToString();
                        break;
                    case "obj":
                        result = ToObject(binder.ReturnType);
                        break;
                    default:
                           result = this.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod, null, this, args);
                        return true;
                        //result = null;
                        //return false;

                }
                return true;
            }
            catch (Exception ex)
            {
                throw new MissingMemberException($"没有找到指定的成员({binder.Name})，确保名称、大小写以及参数个数、顺序、类型是否匹配", ex);
            }
        }

        /// <summary>
        /// Provides implementation for type conversion operations. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class can override this method to specify dynamic behavior for operations that convert an object from one type to another.
        /// </summary>
        /// <param name="binder">Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"></see> class, binder.Type returns the <see cref="T:System.String"></see> type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion.</param>
        /// <param name="result">The result of the type conversion operation.</param>
        /// <returns>
        /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.)
        /// </returns>
        public override bool TryConvert(ConvertBinder binder, out object result)
        {
            if (binder.Type == this.GetType())
            {
                result = this;
                return true;
            }

            if (typeof(IDictionary<string, object>).GetTypeInfo().IsAssignableFrom(binder.Type.GetTypeInfo()))
            {
                result = this.ToDictionary();
                return true;
            }
            if (typeof(IEnumerable<KeyValuePair<string, object>>).GetTypeInfo().IsAssignableFrom(binder.Type.GetTypeInfo()))
            {
                result = this.ToList();
                return true;
            }
            if (binder.Type == typeof(string))
            {
                result = this.ToString();
                return true;
            }
            if (binder.Type == typeof(NameValueCollection))
            {
                result = this.ToNameValueCollection();
                return true;
            }

            result = this.ToObject(binder.Type);
            return true;
        }
        #endregion

        #region GetHashCode
        /// <summary>
        /// 获取当前实例的哈希码。
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode() => _keyValuePairs.GetHashCode();
        #endregion

        #region Equals
        /// <summary>
        /// 表示当前集合是否与指定的对象相等。
        /// </summary>
        /// <param name="obj">一个由 <see cref="KeyValueCollection"/> 可支持的对象。</param>
        /// <returns>
        ///   若相等，则返回 <c>true</c>；否则返回 <c>false</c>。
        /// </returns>
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            var compareCollection = new KeyValueCollection(obj);

            return this.GetHashCode() == compareCollection.GetHashCode();
        }
        #endregion

        /// <summary>
        /// 表示当前集合是否与指定的对象完全匹配，包括所有的键和值。
        /// </summary>
        /// <param name="obj">一个由 <see cref="KeyValueCollection"/> 可支持的对象。</param>
        /// <returns>
        ///   若完全匹配，则返回 <c>true</c>；否则返回 <c>false</c>。
        /// </returns>
        public bool FullEquals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            var compareCollection = new KeyValueCollection(obj);
            foreach (var item in this)
            {
                foreach (var compareItem in compareCollection)
                {
                    return item.Key.Equals(compareItem.Key) && item.Value.Equals(compareItem.Value);
                }
            }
            return false;
        }
    }
}
